package com.linjiang.spring;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 自定义容器
 * @author linjiang
 */
public class LinJiangApplicationContext {
    private Class configClass;

    /**
     * 单例池, 用于存储单例对象
     */
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();

    /**
     * 扫描加载的bean定义对象的集合
     */
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    /**
     * BeanPostProcessor的列表，bean的后置处理器
     */
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    /**
     * 容器的构造方法，接收bean的配置类，并解析
     * @param configClass 配置类
     */
    public LinJiangApplicationContext(Class configClass){
        if(configClass == null){
            return;
        }
        this.configClass = configClass;
        //解析配置类并扫描类
        //ComponentScan注解 ---> 扫描路径 --> 扫描-->BeanDefinition--->BeanDefinitionMap
        scan(configClass);

        //遍历bean定义对象集合，针对单例bean进行处理
        for (Map.Entry<String, BeanDefinition> entry: beanDefinitionMap.entrySet()){
            BeanDefinition beanDefinition = entry.getValue();
            if (beanDefinition.getScope().equals("singleton")){
                //创建bean
                Object bean = createBean(entry.getKey(), beanDefinition);
                //将创建好的单例bean对象放入单例池中
                singletonObjects.put(entry.getKey(), bean);
            }
        }
    }

    /**
     * 根据bean定义对象创建bean
     * @return
     */
    public Object createBean(String beanName, BeanDefinition definition){
        Class clazz = definition.getClazz();
        try {
            //1、bean的创建和依赖注入
            //获取类的无参构造器实例化对象
            Object instance = clazz.getDeclaredConstructor().newInstance();
            //对对象的属性进行初始化，如依赖注入
            //获取类的所有声明的属性字段
            for (Field declaredField : clazz.getDeclaredFields()) {
                //判断该属性字段，是否包含自动注入注解
                if (declaredField.isAnnotationPresent(Autowired.class)){
                    declaredField.setAccessible(true);
                    //先获取要注入的对象实例，先根据bean名称查找
                    String fieldBeanName = declaredField.getName();
                    //调用getBean方法获取要注入的bean的实例
                    Object bean = getBean(fieldBeanName);
                    //给当前对象的这个字段赋值
                    declaredField.set(instance, bean);
                }
            }


            //2、Aware回调
            //判断当前这个实例是否实现了BeanNameAware这个接口
            if(instance instanceof BeanNameAware){
                BeanNameAware aware = (BeanNameAware) instance;
                //调用方法，对beanName进行赋值
                aware.setBeanName(beanName);
            }

            //bean初始化前，遍历后置处理器列表，调用后置处理器相应方法
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
            }

            //3、初始化
            //判断当前这个实例是否实现了InitializingBean接口
            if (instance instanceof InitializingBean){
                //调用实例的实现的afterPropertiesSet方法
                ((InitializingBean) instance).afterPropertiesSet();
            }

            //bean初始化后，遍历后置处理器列表，调用后置处理器相应方法
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);
            }

            return instance;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 解析配置类并扫描
     * @param configClass
     */
    private void scan(Class configClass) throws RuntimeException {
        //解析配置类，判断这个类中是否有相关的注解
        if (configClass.isAnnotationPresent(ComponentScan.class)){
            //ComponentScan注解--->获取扫描路径--->扫描
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
            //如果存在则进行下一步操作
            //解析注解的属性值，获取组件扫描路径
            String path = componentScanAnnotation.value();
            System.out.println("组件扫描路径："+path); //com.linjiang.service

            //拿到包扫描路径后，去扫描获取这个包路径下 有@Component注解的类，并解析
            //扫描用到类加载器
            //BootStrap -->jre/lib
            //Ext --> jre/ext/lib
            //App ---> classpath
            //获取App类加载器
            ClassLoader classLoader = LinJiangApplicationContext.class.getClassLoader();
            //根据路径获取资源目录
            URL resource = classLoader.getResource(path.replace(".","/")); // com/linjiang/service
            File file = new File(resource.getFile());
            System.out.println("文件目录："+file);
            if (file.isDirectory()){
                //获取到资源是一个目录，获取这个目录下的所有文件
                File[] files = file.listFiles();
                for (File f : files) {
                    System.out.println("目录下文件："+f);
                    String fileName = f.getAbsolutePath();//文件的绝对路径 G:\www\Spring-Learn\target\classes\com\linjiang\service\MyService.class
                    //判断该文件是否是class文件
                    if(fileName.endsWith(".class")){
                        String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".")).replace("\\",".");
                        System.out.println("处理后的类加载路径："+className); //com.linjiang.service.MyService
                        try {
                            //用app类加载器，根据类文件的路径加载类
                            Class<?> aClass = classLoader.loadClass(className);//传入的name为 com.linjiang.service.UserService
                            //判断该类是否存在注解@Component
                            if(aClass.isAnnotationPresent(Component.class)){
                                // 表示当前这个类是一个bean
                                //通过注解获取bean的名字
                                Component component = aClass.getDeclaredAnnotation(Component.class);
                                String beanName = component.name().isEmpty() ? aClass.getSimpleName() : component.name();
                                if("".equals(beanName)){
                                    //如何为空，则根据类名的首字母小写生成
                                    beanName = Introspector.decapitalize(aClass.getSimpleName());
                                }

                                //判断当前类 是否实现了BeanPostProcessor接口
                                if (BeanPostProcessor.class.isAssignableFrom(aClass)) {
                                    //直接实例化该bean
                                    BeanPostProcessor instance = (BeanPostProcessor) aClass.getDeclaredConstructor().newInstance();
                                    //放入后置处理器列表
                                    beanPostProcessorList.add(instance);
                                }

                                //解析当前类--> 生成一个对应BeanDefinition对象
                                BeanDefinition beanDefinition = new BeanDefinition();
                                beanDefinition.setClazz(aClass);

                                //判断该bean的作用域，看是否的单例还是原型
                                if (aClass.isAnnotationPresent(Scope.class)){
                                    Scope scope = aClass.getDeclaredAnnotation(Scope.class);
                                    beanDefinition.setScope(scope.value());
                                }else {
                                    //默认单例模式
                                    beanDefinition.setScope("singleton");
                                }
                                //将bean定义对象存入map，key为bean的名称
                                beanDefinitionMap.put(beanName, beanDefinition);
                            }
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        } catch (NoSuchMethodException | InvocationTargetException | InstantiationException |
                                 IllegalAccessException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
        }
    }

    /**
     * 获取bean的实例
     * @param beanName
     * @return
     */
    public Object getBean(String beanName){
        //根据bean定义对象，判断bean的作用域
        if (beanDefinitionMap.containsKey(beanName)){
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if ("singleton".equals(beanDefinition.getScope())){
                //是单例bean，则从容器的单例池中取出该bean返回
                Object singletonObject = singletonObjects.get(beanName);
                if (singletonObject == null){
                    //为空则创建
                    singletonObject = createBean(beanName, beanDefinition);
                    singletonObjects.put(beanName, singletonObject);
                }
                return singletonObject;
            }else {
                //是原型，创建bean对象
                return createBean(beanName, beanDefinition);
            }
        }else {
            //不存在该bean的定义
            throw new NullPointerException();
        }
    }
}
